#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
  this->variable=variable;
  this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() { 
  int valeur=0;
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();
  if      (this->operateur=="+") valeur = og+od; 
  else if (this->operateur=="-") valeur = og-od;
  else if (this->operateur=="*") valeur = og*od; 
  else if (this->operateur == "et") valeur = (og and od);
  else if (this->operateur == "ou") valeur = (og or od);
  else if (this->operateur == "==") valeur = (og == od);
  else if (this->operateur == "<") valeur = (og < od);
  else if (this->operateur == "<=") valeur = (og <= od);
  else if (this->operateur == ">") valeur = (og > od);
  else if (this->operateur == ">=") valeur = (og >= od);
  else if (this->operateur == "!=") valeur = (og != od);
  else {
        if (od!=0)
		valeur = og/od;
  	else {
	  	cout << "Erreur pendant l'interpretation : division par zero" << endl;
	  	exit(0); // plus tard on levera une exception
  	}
  }
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}



////////////////////////////////////////////////////////////////////////////////
//Noeud Inst Si
////////////////////////////////////////////////////////////////////////////////
NoeudInstSi::NoeudInstSi() :tabSeq(), tabCond()  {
    
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstSi::afficher(unsigned short indentation) {
    
        Noeud::afficher(indentation);
        cout << "Noeud : Inst Si";
        
        unsigned int i;
        
        for (i=0; i<tabCond.size(); i++) {

                tabCond[i]->afficher(indentation + 1);
                tabSeq[i]->afficher(indentation + 1);

        }

        if( i+1 == tabSeq.size() ){
             tabSeq[tabSeq.size() - 1]->afficher(indentation + 1);
       }
}

//////////////////////////////////////////////////////////////////////////////////
vector<Noeud *> NoeudInstSi::getCond() {
        return tabCond;
}

////////////////////////////////////////////////////////////////////////////////

vector<Noeud *> NoeudInstSi::getSeq() {
        return tabSeq;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstSi::getValeur() {
 //on récupère les valeurs du Noeud InstSi   
        bool fin = false;
        for (unsigned int i=0; i<tabCond.size(); i++) {            // on parcours tabCond
                if (tabCond[i] != NULL && !fin) {                                 // si on n'est pas encore arrivé au sinon et qu'aucun test n'est vrai avant
                        if (tabCond[i]->getValeur()) {                                     // PARCOURS RECURSIF de l'arbre afin de récupérer toutes les valeurs
                                tabSeq[i]->getValeur();                                    // De même pour tabSeq
                                fin = true;                                                //pour sortir de la boucle
                        }
                } else {
                        if (!fin)                                                           // SINON
                                tabSeq[i]->getValeur();                                    //seqInst du sinon
                }
        }
        return 0;
}


////////////////////////////////////////////////////////////////////////////////

void NoeudInstSi::ajouteCondition(Noeud* condition) {
    //ajoute une instruction au noeud
     tabCond.push_back(condition);
}


////////////////////////////////////////////////////////////////////////////////


void NoeudInstSi::ajouteInstruction(Noeud* seq) {
    //ajoute une instruction au noeud
    tabSeq.push_back(seq);
}


////////////////////////////////////////////////////////////////////////////////

NoeudInstTantque::NoeudInstTantque(Noeud* condition, Noeud * sequence) {
    this->condi=condition;
    this->sequ=sequence;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstTantque::getValeur(){
    // on récupère les valeurs du noeud inst tant que
        while (condi->getValeur()) //on parcours le noeud en récursif
                sequ->getValeur();
        return 0;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstTantque::afficher(unsigned short indentation){
        Noeud::afficher(indentation);
        cout << "Noeud : InstTq"
                        << "\" appliqué à : " << endl;
        condi->afficher(indentation + 1); // on affiche fils gauche et droit 
        sequ->afficher(indentation + 1);  // avec +1 pour l'indention
}

////////////////////////////////////////////////////////////////////////////////

NoeudInstRepeter::NoeudInstRepeter( Noeud * sequence , Noeud* condition) {
    this->seque=sequence;
    this->condit=condition;
}
////////////////////////////////////////////////////////////////////////////////

int NoeudInstRepeter::getValeur(){
        do
                seque->getValeur();
        while (condit->getValeur()); //on parcours le noeud en récursif
        
        return 0;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstRepeter::afficher(unsigned short indentation){

    Noeud::afficher(indentation);
        cout << "Noeud : InstTq"
                        << "\" appliqué à : " << endl;
        condit->afficher(indentation + 1); // on affiche fils gauche et droit 
        seque->afficher(indentation + 1);  // avec +1 pour l'indention
 
}


////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(Noeud* affectation, Noeud* condition, Noeud* affectation1, Noeud* sequence) {
        this->aff = affectation;
        this->cond = condition;
        this->aff1 = affectation1;
        this->sequ = sequence;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstPour::getValeur() {

        aff->getValeur();
        while (cond->getValeur()) {
                sequ->getValeur();
                aff1->getValeur();
        }
        return 0;
}

////////////////////////////////////////////////////////////////////////////////


void NoeudInstPour::afficher(unsigned short indentation) {
        Noeud::afficher(indentation);
        cout << "Noeud - InstPour \" pour"
                        << "\" applique a : " << endl;
        cond->afficher(indentation + 1);
        aff1->afficher(indentation + 1);
        sequ->afficher(indentation + 1);

}

////////////////////////////////////////////////////////////////////////////////


NoeudLire::NoeudLire(Noeud* variable){
    this->NoeudVariable=variable;
    }

////////////////////////////////////////////////////////////////////////////////       
int NoeudLire:: getValeur(){
    
        int valeur = 0;
        cout << "Valeur de " << ((SymboleValue*)this->NoeudVariable)->getChaine() << " : ";
        cin >> valeur;
        ((SymboleValue*) this->NoeudVariable)->setValeur(valeur);            // on affecte la variable
        return 0;

}
////////////////////////////////////////////////////////////////////////////////

void NoeudLire::afficher(unsigned short indentation)
{
        indentation=0;
}
/////////////////////////////////////////////////////////////////////////////////   

  NoeudEcrire::NoeudEcrire(Noeud* expression){
    this->exp = expression;  
}
////////////////////////////////////////////////////////////////////////////////   
  
int NoeudEcrire:: getValeur(){
        int valeur = 0;
        valeur = ((SymboleValue*)this->exp)->getValeur();
        cout << valeur;
        return 0;

}
////////////////////////////////////////////////////////////////////////////////

void NoeudEcrire::afficher(unsigned short indentation)
{
        Noeud::afficher(indentation);
        cout << "Noeud - Ecrire" << endl;
        exp->afficher(indentation + 1); 

}


/////////////////EXTENTIONS///////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// NoeudInstSwitch
////////////////////////////////////////////////////////////////////////////////

NoeudInstSwitch::NoeudInstSwitch(Noeud* var) :
        NoeudInstSi() {
                this->variable = var;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstSwitch::afficher(unsigned short indentation) {
        Noeud::afficher(indentation);
        unsigned int i = 0;
        cout << "Noeud - InstSwitch \" Switch"
                        << "\" applique a : " << endl;
        cout << variable->getValeur();
        vector<Noeud *> cond = getCond();
        vector<Noeud *> seq = getSeq();
        
        for (i = 0; i<cond.size(); i++) {
                cond[i]->afficher(indentation + 1);                     // on affiche fils gauche et fils droit
                seq[i]->afficher(indentation + 1);                      // en augmentant l'indentation
        }
        
       if( i+1 == seq.size() ){
             seq[seq.size() - 1]->afficher(indentation + 1);
       }
}



////////////////////////////////////////////////////////////////////////////////


